﻿using System;
using System.Data;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

using RAD.LightOne;
using RAD.LightOne.Data;
using RAD.LightOne;

namespace RAD.LightOne
{
    #region Documentation
    /// <summary>
    /// Classe base para o objeto "malandro", que terá seu valor carregado tão somente quando acessado.
    /// </summary>
    /// <typeparam name="T">Tipo T genérico que é armazenado em Lazy e deve herdar de MyObject.</typeparam>
    #endregion
    [DefaultProperty("Value")]
    public sealed class Lazy<T> : Lazy, ILazy where T : MyObject
    {
        #region Events

        public event TransactionHandler Loaded;

        public event TransactionHandler Loading;

        #endregion Events

        #region Fields

        private Guid id;
        private T value;

        #endregion Fields

        #region Properties

        #region Documentation
        /// <summary>
        /// Define/Retorna a transção de banco a ser utilizada nas operações de dados.
        /// </summary>
        #endregion
        public IDbTransaction Trx
        {
            get;
            set;
        }

        public override Type Type
        {
            get
            {
                return typeof(T);
            }
        }

        public T Value
        {
            get
            {
                if (this.id != Guid.Empty)
                {
                    if (this.Trx == null)
                    {
                        Load();
                    }
                    else
                    {
                        this.Load(this.Trx);
                    }
                }
                return this.value;
            }
        }

        #region Documentation
        ///<summary>
        /// Id armazanado referente ao Id do objeto contido.
        ///</summary>
        #endregion
        public Guid Id
        {
            get
            {
                if (this.value != null) return this.value.Id;
                return this.id;
            }
        }

        #endregion Properties

        #region Constructors

        public Lazy() : base() { }

        public Lazy(T value) : base()
        {
            this.value = value;
            this.id = Guid.Empty;
        }

        public Lazy(Guid id)
        {
            this.value = null;
            this.id = id;
        }

        #endregion Constructors

        #region Methods

        public void Set(T value)
        {
            this.value = value;
            this.id = Guid.Empty;
        }

        public override void  Set(Guid id)
        {
            this.id = id;
            this.value = null;
        }

        public void Load()
        {
            if (this.Trx == null)
            {
                using (DBEngine dbe = DBEngine.GetInstance())
                {
                    using (IDbTransaction trx = dbe.BeginTransaction())
                    {
                        try
                        {
                            this.Load(trx);
                            trx.Commit();

                        }
                        catch (Exception except)
                        {
                            trx.Rollback();
                            throw except;
                        }
                    }
                }
            }
            else
            {
                this.Load(this.Trx);
            }
        }

        internal void Load(IDbTransaction trx)
        {
            if (this.Loading != null) this.Loading(trx);

            this.Set(MyObject<T>.FindById(trx, this.id));

            if (this.Loaded != null) this.Loaded(trx);
        }

        #endregion Methods

        #region ILazy Members

        [Obsolete("invalid in this context",true)]
        void ILazy.Set(MyObject value)
        {
            throw new NotImplementedException();
        }

        MyObject ILazy.Value
        {
            get
            {
                return this.value;
            }
        }

        #endregion

        #region Operators

        public static implicit operator T(Lazy<T> source)
        {
            return source.Value;
        }

        public static implicit operator Lazy<T>(T source)
        {
            return new Lazy<T>(source);
        }
        #endregion
    }
}
